package com.coldworks.base.manager;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigDecimal;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory.Options;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.Thumbnails;
import android.util.Log;

import com.coldworks.base.util.BaseCONST;

public class BaseBitmapManager {
	protected final Logger log = LoggerFactory.getLogger(this.getClass());
	private static BaseBitmapManager instance = null;

	public static BaseBitmapManager getInstance() {
		if (instance == null) {
			synchronized (BaseBitmapManager.class) {
				if (instance == null) {
					instance = new BaseBitmapManager();
				}
			}
		}
		return instance;
	}

	private BaseBitmapManager() {

	}

	/**
	 * 获得设置信息
	 **/
	public Options getOptions(String path) {
		Options options = new Options();
		options.inJustDecodeBounds = true; // 只描边，不读取数据
		BitmapFactory.decodeFile(path, options);
		return options;
	}

	/**
	 * 获得设置信息
	 **/
	public Options getOptions(File file) {
		return getOptions(file.getAbsolutePath());
	}

	/**
	 * 获得图像
	 * 
	 * @param path
	 * @param options
	 * @return
	 * @throws FileNotFoundException
	 */

	public int dpToPx(Context context, int dp) {
		float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dp * scale + 0.5f);
	}

	public Bitmap getBitmapByPath(Context context, String path,
			Options options, int screenWidth, int screenHeight, boolean clear) {
		File file = new File(path);
		return getBitmapByFile(context, file, options, screenWidth,
				screenHeight, clear);
	}

	public Bitmap getBitmapByFile(Context context, File file, Options options,
			int desireWidth, int desireHeight, boolean clear) {
		if (!file.exists()) {
			return null;
		}
		FileInputStream in = null;
		try {
			in = new FileInputStream(file);
			if (options != null) {
				int desireMax = Math.max(desireWidth, desireHeight);
				int desireTotal = desireWidth * desireHeight;
				int picWidth = options.outWidth;
				int picHeight = options.outHeight;
				int picTotal = picWidth * picHeight;
				int initialSampleSize;
				int sampleByTotal = 0;
				int sampleByWidth = 0;

				if (desireTotal <= 0 || desireMax <= 0) {
					initialSampleSize = 1;
				} else {
					double totalScale = Math.sqrt((double) picTotal
							/ (double) desireTotal);
					double widthScale = (double) picWidth
							/ (double) desireWidth;
					if (clear) {
						sampleByTotal = (int) Math.floor(totalScale);
						sampleByWidth = (int) Math.floor(widthScale);
					} else {
						sampleByTotal = (int) sswr(totalScale, 0);
						sampleByWidth = (int) sswr(widthScale, 0);
					}
					initialSampleSize = Math.max(sampleByTotal, sampleByWidth);
				}
				int inSampleSize = computeSampleSize(initialSampleSize);
				if (inSampleSize == 1) {
					while (picTotal > BaseCONST.BITMAP.MAX_BITMAP_PIXELS) {
						inSampleSize <<= 1;
						picTotal >>= 2;
					}
				}

				log.info("clear:" + clear + " iss:" + initialSampleSize + "->"
						+ inSampleSize + " desire:" + desireWidth + "x"
						+ desireHeight + " pic:" + options.outWidth + "x"
						+ options.outHeight + " sampleByTotal:" + sampleByTotal
						+ " sampleByEdge:" + sampleByWidth);
				options.inSampleSize = inSampleSize; // 设置缩放比例
				options.inJustDecodeBounds = false;
			}
			return BitmapFactory.decodeStream(in, null, options);
		} catch (OutOfMemoryError e) {
			log.error("", e);
			System.gc();
		} catch (IOException e) {
			log.error("", e);
		} finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (IOException e) {
				log.error("", e);
			}
		}
		return null;
	}

	private static double sswr(double value, int scale) {
		return new BigDecimal(Double.toString(value)).divide(
				new BigDecimal("1"), scale, BigDecimal.ROUND_HALF_UP)
				.doubleValue();
	}

	/**
	 * 获取需要进行缩放的比例，即options.inSampleSize
	 * 
	 * @param options
	 * @return
	 */
	public int computeSampleSize(int initialSize) {
		int roundedSize;
		if (initialSize <= 8) {
			roundedSize = 1;
			while (roundedSize < initialSize) {
				roundedSize <<= 1;
			}
		} else {
			roundedSize = (initialSize + 7) / 8 * 8;
		}
		return roundedSize;
	}

	public long createContent(final Context context, File destFile) {
		try {
			long time = System.currentTimeMillis();
			ContentValues values = new ContentValues(7);
			values.put(Images.Media.TITLE, destFile.getName());
			values.put(Images.Media.DISPLAY_NAME, destFile.getName());
			values.put(Images.Media.DATE_ADDED, time);
			values.put(Images.Media.MIME_TYPE, "image/jpeg");
			values.put(Images.Media.DATA, destFile.getAbsolutePath());
			final Uri uri = context.getContentResolver().insert(
					MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
			final long imgId = ContentUris.parseId(uri);
			new Thread() {
				@Override
				public void run() {
					// log.info(context, "createContent", "id:" +
					// ContentUris.parseId(uri)
					// + " uri" + uri.toString());
					Bitmap bitmap = getThumbnailById(context, imgId, 96, 96);
					if (bitmap != null && !bitmap.isRecycled()) {
						bitmap.recycle();
						bitmap = null;
					}
				}
			}.start();
			return imgId;
		} catch (Exception e) {
			log.error("", e);
		}
		return 0L;
	}

	public void deleteContentResolver(Context context, long imgId) {
		try {
			context.getContentResolver()
					.delete(ContentUris
							.withAppendedId(
									MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
									imgId),
							null, null);
		} catch (Exception e) {
			log.error("", e);
		}
	}

	public Bitmap getThumbnailById(Context context, long imgId, int imgWidth,
			int imgHeight) {
		try {
			Bitmap bitmap = null;
			BitmapFactory.Options opt = new BitmapFactory.Options();
			opt.inPreferredConfig = Bitmap.Config.RGB_565;
			opt.outWidth = imgWidth;
			opt.outHeight = imgHeight;
			bitmap = MediaStore.Images.Thumbnails.getThumbnail(
					context.getContentResolver(), imgId, Thumbnails.MICRO_KIND,
					opt);
			return bitmap;
		} catch (Exception e) {
			log.error("", e);
		}
		return null;
	}

	public Bitmap getThumbnailByPath(Context context, String pathName,
			int imgWidth, int imgHeight) {
		try {
			Bitmap bitmap = null;
			BitmapFactory.Options opts = new BitmapFactory.Options();
			opts.inPreferredConfig = Bitmap.Config.RGB_565;
			opts.inJustDecodeBounds = true;
			opts.inTempStorage = new byte[16 * 1024];
			BitmapFactory.decodeFile(pathName, opts);
			opts.inSampleSize = computeThumnailSampleSize(context,
					opts.outWidth, opts.outHeight);
			opts.inJustDecodeBounds = false;
			opts.inTempStorage = new byte[16 * 1024];
			bitmap = BitmapFactory.decodeFile(pathName, opts);
			return bitmap;
		} catch (Exception e) {
			log.error("", e);
		}
		return null;
	}

	public int computeThumnailSampleSize(Context context, int imgWidth,
			int imgHeight) {
		BaseDisplayManager dm = BaseDisplayManager.getInstance();
		int screenWidth = dm.getDisplayWidth(context);
		int screenHeight = dm.getDisplayHeight(context);
		int imgMax = Math.max(imgHeight, imgWidth);
		int screenMax = Math.max(screenHeight, screenWidth);
		int sampleSize = 1;
		while (imgMax > screenMax / 4) {
			imgMax = imgMax / 2;
			sampleSize = sampleSize * 2;
		}
		return sampleSize;
	}

	public long getImgIdByPath(Context context, String pathname) {
		Cursor cursor = null;
		try {
			String[] projection = { MediaStore.Images.Media._ID,
					MediaStore.Images.Media.DATA };
			String where = MediaStore.Images.Media.DATA + " = '" + pathname
					+ "'";
			cursor = MediaStore.Images.Media.query(
					context.getContentResolver(),
					MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
					where, null);
			cursor.moveToFirst();
			if (cursor.isAfterLast()) {
				return 0L;
			} else {
				return cursor.getLong(cursor
						.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
			}
		} catch (Exception e) {
			log.error("", e);
		} finally {
			if (cursor != null) {
				try {
					cursor.close();
				} catch (Exception e) {
					log.error("", e);
				}
			}
		}
		return 0L;
	}

	public Cursor findAllImg(Context context) {
		String[] projection = { MediaStore.Images.Media._ID,
				MediaStore.Images.Media.DATA };
		return MediaStore.Images.Media.query(context.getContentResolver(),
				MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
				MediaStore.Images.Media.DATE_MODIFIED + " desc");
	}

	public Cursor getImgsCursorByDir(Context context, File dir) {
		try {
			String[] projection = { MediaStore.Images.Media._ID,
					MediaStore.Images.Media.DATA };
			String where = MediaStore.Images.Media.DATA + " like '%"
					+ dir.getAbsolutePath() + "%'";
			return MediaStore.Images.Media.query(context.getContentResolver(),
					MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
					where, MediaStore.Images.Media.DATE_ADDED + " desc");
		} catch (Exception e) {
			log.error("", e);
		}
		return null;
	}

	public Bitmap getBitmapByFile(Context context, File file, int width,
			int height) {
		if (file == null || !file.exists() || !file.isFile() || !file.canRead()) {
			return null;
		}
		Bitmap bitmap = null;
		BitmapFactory.Options opt = null;
		InputStream is = null;
		try {
			is = context.getContentResolver().openInputStream(
					Uri.fromFile(file));
			opt = new BitmapFactory.Options();
			opt.inPreferredConfig = Bitmap.Config.RGB_565;
			opt.inJustDecodeBounds = true;
			opt.inTempStorage = new byte[16 * 1024];
			BitmapFactory.decodeFile(file.getAbsolutePath(), opt);
			int srcWidth = opt.outWidth;
			int srcHeight = opt.outHeight;
			int iws = (int) (srcWidth / width);
			if (iws <= 0) {
				iws = 1;
			}
			int ihs = (int) (srcHeight / height);
			if (ihs <= 0) {
				ihs = 1;
			}
			int iss = Math.min(iws, ihs) + 1;
			if (iss <= 2) {
				iss = 6;
			} else if (iss <= 6) {
				iss = 12;
			} else if (iss <= 12) {
				iss = 16;
			} else {
				iss = 24;
			}
			opt.inSampleSize = iss;
			opt.inJustDecodeBounds = false;
			opt.inPurgeable = true;
			opt.inInputShareable = true;
			bitmap = BitmapFactory.decodeStream(is, null, opt);
			if (bitmap == null) {
				return null;
			}
			int w = bitmap.getWidth();
			int h = bitmap.getHeight();
			int s = Math.min(w, h);
			int temp = (int) (Math.abs(w - h) / 2);
			Bitmap resBitmap = null;
			if (w > h) {
				resBitmap = Bitmap.createBitmap(bitmap, temp, 0, s, s);
			} else {
				resBitmap = Bitmap.createBitmap(bitmap, 0, temp, s, s);
			}

			ByteArrayOutputStream os = new ByteArrayOutputStream();
			resBitmap.compress(CompressFormat.JPEG, 80, os);
			byte[] datas = os.toByteArray();
			if (datas != null) {
				return BitmapFactory.decodeByteArray(datas, 0, datas.length);
			}
		} catch (OutOfMemoryError e) {
			if (bitmap != null) {
				bitmap.recycle();
				bitmap = null;
			}
			System.gc();
			log.error("", e);
		} catch (Exception e) {
			log.error("", e);
		} finally {
			if (opt != null) {
				opt.inTempStorage = null;
				opt = null;
			}
			if (is != null) {
				try {
					is.close();
				} catch (Exception e) {
					log.error("", e);
				}
			}
		}
		return null;
	}

	public Bitmap getBitmapByFile(Context context, File file) {
		if (file == null || !file.exists() || !file.isFile() || !file.canRead()) {
			return null;
		}
		try {
			BitmapFactory.Options opt = null;
			Bitmap bitmap = null;
			if (file.exists() && file.canRead()) {
				InputStream is = null;
				try {
					is = context.getContentResolver().openInputStream(
							Uri.fromFile(file));
					opt = new BitmapFactory.Options();
					opt.inTempStorage = new byte[BaseCONST.HTTP.BUFFER_SIZE];
					opt.inScaled = false;
					opt.inPurgeable = true;
					opt.inInputShareable = true;
					bitmap = BitmapFactory.decodeStream(is, null, opt);
				} catch (OutOfMemoryError e) {
					if (bitmap != null) {
						bitmap.recycle();
						bitmap = null;
					}
					System.gc();
					log.error("", e);
				} catch (Exception e) {
					log.error("", e);
				} finally {
					if (opt != null) {
						opt.inTempStorage = null;
						opt = null;
					}
					if (is != null) {
						try {
							is.close();
						} catch (Exception e) {
							log.error("", e);
						}
					}
				}
			}
			return bitmap;
		} catch (Exception e) {
			log.error("", e);
		}
		return null;
	}

	public Bitmap getBitmapByResid(Context context, int resId) {
		try {
			BitmapFactory.Options opt = new BitmapFactory.Options();
			opt.inScaled = false;
			opt.inPurgeable = true;
			opt.inInputShareable = true;
			InputStream is = context.getResources().openRawResource(resId);
			return BitmapFactory.decodeStream(is, null, opt);
		} catch (Exception e) {
			log.error("", e);
		}
		return null;
	}

	public byte[] getBytesByFileName(String fileName, int offset, int len) {
		if (fileName == null) {
			return null;
		}
		File file = new File(fileName);
		if (!file.exists()) {
			Log.i("ImageUtil", "readFromFile: file not found");
			return null;
		}
		if (len == -1) {
			len = (int) file.length();
		}
		Log.d("ImageUtil", "readFromFile : offset = " + offset + " len = "
				+ len + " offset + len = " + (offset + len));
		if (offset < 0) {
			Log.e("ImageUtil", "readFromFile invalid offset:" + offset);
			return null;
		}
		if (len <= 0) {
			Log.e("ImageUtil", "readFromFile invalid len:" + len);
			return null;
		}
		if (offset + len > (int) file.length()) {
			Log.e("ImageUtil", "readFromFile invalid file len:" + file.length());
			return null;
		}
		byte[] b = null;
		try {
			RandomAccessFile in = new RandomAccessFile(fileName, "r");
			b = new byte[len];
			in.seek(offset);
			in.readFully(b);
			in.close();
		} catch (Exception e) {
			Log.e("ImageUtil", "readFromFile : errMsg = " + e.getMessage());
			e.printStackTrace();
		}
		return b;
	}

	public String getFileNameByUrl(String url) {
		int lastIndex = url.lastIndexOf("/");
		if (lastIndex >= 0) {
			return url.substring(lastIndex + 1);
		} else {
			return null;
		}
	}

	public boolean downBitmapByUrl(Context context, String url, String imagePath) {
		File file = new File(imagePath);
		return downBitmapByUrl(context, url, file);
	}

	public boolean downBitmapByUrl(Context context, String url, File file) {
		if (file.exists()) {
			file.delete();
		}
		try {
			// log.info(file.getAbsolutePath());
			file.createNewFile();
		} catch (IOException e) {
			log.error("", e);
			return false;
		}
		int count = 0;
		InputStream is = null;
		FileOutputStream fos = null;

		try {
			HttpResponse response = BaseNetworkManager.getInstance()
					.httpGETResponse(context, url);
			if (response == null) {
				log.warn("no response");
				return false;
			}
			HttpEntity entity = response.getEntity();
			if (entity == null) {
				log.warn("no entity");
				return false;
			}
			int size = (int) entity.getContentLength();
			if (BaseStorageManager.getInstance().getAvailableStore(
					BaseStorageManager.getInstance().getSdRoot()) < size) {
				return false;
			}
			is = entity.getContent();
			if (is == null) {
				file.delete();
				return false;
			}
			fos = new FileOutputStream(file);
			byte[] buf = new byte[BaseCONST.HTTP.BUFFER_SIZE];
			int ch = -1;
			while ((ch = is.read(buf)) != -1) {
				count += ch;
				fos.write(buf, 0, ch);
			}
			fos.flush();
			if (fos != null)
				fos.close();
			if (count == size) {
				return true;
			} else {
				file.delete();
				return false;
			}
		} catch (IOException e) {
			log.error("", e);
			file.delete();
			return false;
		} catch (Exception e) {
			log.error("", e);
			file.delete();
			return false;
		} finally {
			try {
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				log.error("", e);
			}
			try {
				if (fos != null) {
					fos.close();
				}
			} catch (IOException e) {
				log.error("", e);
			}
		}

	}

	public boolean fileIsGIF(String imgUrl) {
		int lastIndex = imgUrl.lastIndexOf(".");
		String ext = imgUrl.substring(lastIndex + 1);
		if (ext.equals("gif")) {
			return true;
		}
		return false;
	}

	public boolean fileIsImage1(File f) {
		if (f.getName().endsWith(".jpg") || f.getName().endsWith(".jpeg")
				|| f.getName().endsWith(".bmp") || f.getName().endsWith(".png")
				|| f.getName().endsWith(".JPG") || f.getName().endsWith(".BMP")
				|| f.getName().endsWith(".PNG")
				|| f.getName().endsWith(".JPEG")) {
			return true;
		} else {
			return false;
		}
	}

	public boolean fileIsImage2(File f) {
		if (!f.isFile() || !f.canRead()) {
			return false;
		}
		String name = f.getName();
		int cur = name.lastIndexOf('.');
		if (cur == -1) {
			return false;
		}
		String ext = name.substring(cur).toLowerCase();
		if ((ext.endsWith(".jpg") || (ext.endsWith(".jpeg"))
				|| ext.endsWith(".bmp") || ext.endsWith("png"))
				&& f.length() > 30000) {
			return true;
		}
		return false;
	}

	public boolean fileIsImage3(File file) {
		FileInputStream fis = null;
		try {
			// 从SDCARD下读取一个文件
			fis = new FileInputStream(file);
			byte[] buffer = new byte[2];
			// 文件类型代码
			String filecode = "";
			// 文件类型
			// 通过读取出来的前两个字节来判断文件类型
			if (fis.read(buffer) != -1) {
				for (int i = 0; i < buffer.length; i++) {
					// 获取每个字节与0xFF进行与运算来获取高位，这个读取出来的数据不是出现负数
					// 并转换成字符串
					filecode += Integer.toString((buffer[i] & 0xFF));
				}
				// 把字符串再转换成Integer进行类型判断
				switch (Integer.parseInt(filecode)) {
				case 7790:
					// fileType = "exe";
					break;
				case 7784:
					// fileType = "midi";
					break;
				case 8297:
					// fileType = "rar";
					break;
				case 8075:
					// fileType = "zip";
					break;
				case 255216:
					// fileType = "jpg";
					return true;
				case 7173:
					// fileType = "gif";
					break;
				case 6677:
					// fileType = "bmp";
					return true;
				case 13780:
					// fileType = "png";
					return true;
				default:
					// fileType = "unknown type: " + filecode;
				}
			}
		} catch (FileNotFoundException e) {
			log.error("", e);
		} catch (IOException e) {
			log.error("", e);
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (Exception e) {
					log.error("", e);
				}
			}
		}
		return false;
	}
}